"""
    ObservationTypes

Comprehensive type system for representing all atmospheric observations used in GSI data 
assimilation. This module defines a sophisticated type hierarchy that captures the full
diversity of meteorological observations while maintaining type safety and computational
efficiency.

# Type Hierarchy

The observation type system is organized hierarchically:

```
AbstractObservation{T}
├── ConventionalObservation{T}
│   ├── SurfaceObservation{T}
│   ├── RadiosondeObservation{T}
│   ├── AircraftObservation{T}
│   └── MarineObservation{T}
├── SatelliteObservation{T}
│   ├── RadianceObservation{T}
│   ├── SatelliteWindObservation{T}
│   └── RetrievalObservation{T}
└── SpecializedObservation{T}
    ├── GPSROObservation{T}
    ├── RadarObservation{T}
    ├── LightningObservation{T}
    └── AerosolObservation{T}
```

# Key Features

- **Type Safety**: Compile-time type checking prevents errors
- **Performance**: Efficient memory layout and specialized methods
- **Extensibility**: Easy to add new observation types
- **Metadata Rich**: Complete observation metadata and quality control information
- **Precision Flexible**: Support for both Float32 and Float64 precision

# Physical Units and Conventions

All observations follow WMO standards and SI units:
- **Temperature**: Kelvin (K)
- **Pressure**: Pascals (Pa)  
- **Humidity**: Specific humidity (kg/kg)
- **Wind**: Meters per second (m/s)
- **Radiance**: Brightness temperature (K)
- **Distance**: Meters (m)
- **Time**: UTC DateTime

# Quality Control Integration

Each observation type includes comprehensive quality control information:
- **Gross error flags**: Physical bounds checking
- **Background check results**: Innovation-based quality control
- **Buddy check flags**: Spatial consistency validation
- **Analysis usage flags**: Whether observation is assimilated
"""
module ObservationTypes

using Dates
using StaticArrays
using ..GSICoreAnalysis: AbstractAnalysisConfig

# Export main types and constructors
export AbstractObservation, ConventionalObservation, SatelliteObservation, SpecializedObservation
export SurfaceObservation, RadiosondeObservation, AircraftObservation, MarineObservation
export RadianceObservation, SatelliteWindObservation, RetrievalObservation
export GPSROObservation, RadarObservation, LightningObservation, AerosolObservation
export ObservationLocation, QualityControlFlags, ObservationError
export ObservationCollection, group_observations, filter_observations
export create_surface_observation, create_radiance_observation, create_radiosonde_observation

"""
    ObservationVariable

Enumeration of all atmospheric variables that can be observed.
"""
@enum ObservationVariable begin
    # Thermodynamic variables
    TEMPERATURE
    PRESSURE  
    SURFACE_PRESSURE
    DEW_POINT
    RELATIVE_HUMIDITY
    SPECIFIC_HUMIDITY
    MIXING_RATIO
    
    # Dynamic variables
    WIND_U
    WIND_V
    WIND_SPEED
    WIND_DIRECTION
    VERTICAL_VELOCITY
    
    # Radiative variables
    BRIGHTNESS_TEMPERATURE
    RADIANCE
    REFLECTANCE
    VISIBLE_RADIANCE
    INFRARED_RADIANCE
    
    # Hydrologic variables
    PRECIPITATION_RATE
    PRECIPITATION_ACCUMULATION
    CLOUD_WATER_CONTENT
    CLOUD_ICE_CONTENT
    CLOUD_FRACTION
    
    # Specialized variables
    GPS_REFRACTIVITY
    GPS_BENDING_ANGLE
    RADAR_REFLECTIVITY
    RADAR_VELOCITY
    LIGHTNING_FLASH_RATE
    AEROSOL_OPTICAL_DEPTH
    OZONE_MIXING_RATIO
    
    # Surface variables
    SKIN_TEMPERATURE
    SEA_SURFACE_TEMPERATURE
    LAND_SURFACE_TEMPERATURE
    SNOW_DEPTH
    ALBEDO
end

"""
    ObservationPlatform

Enumeration of observation platforms and instruments.
"""
@enum ObservationPlatform begin
    # Conventional platforms
    SURFACE_STATION
    RADIOSONDE
    PIBAL
    AIRCRAFT_ACARS
    AIRCRAFT_AMDAR
    SHIP
    BUOY
    DROPSONDE
    
    # Satellite platforms
    NOAA_SERIES
    METOP_SERIES
    GOES_SERIES
    HIMAWARI_SERIES
    METEOSAT_SERIES
    AQUA
    TERRA
    SUOMI_NPP
    JPSS_SERIES
    
    # Specialized platforms
    GPS_RECEIVER
    WEATHER_RADAR
    LIGHTNING_DETECTOR
    LIDAR_SYSTEM
    AERONET_STATION
end

"""
    ObservationLocation{T<:AbstractFloat}

Three-dimensional geographic location with uncertainty information.

# Fields
- `latitude::T`: Geographic latitude in degrees (-90 to +90)
- `longitude::T`: Geographic longitude in degrees (-180 to +180) 
- `altitude::T`: Altitude above mean sea level in meters
- `pressure::T`: Atmospheric pressure level in Pascals (for upper-air observations)
- `location_error::SVector{3,T}`: Location uncertainty [lat_err, lon_err, alt_err] in degrees/meters
"""
struct ObservationLocation{T<:AbstractFloat}
    latitude::T
    longitude::T  
    altitude::T
    pressure::T
    location_error::SVector{3,T}
    
    function ObservationLocation{T}(lat::T, lon::T, alt::T = zero(T), 
                                   pres::T = zero(T),
                                   loc_err::SVector{3,T} = SVector{3,T}(0.01, 0.01, 10.0)) where T
        # Validate latitude bounds
        if abs(lat) > 90
            throw(ArgumentError("Latitude must be between -90 and +90 degrees, got $lat"))
        end
        
        # Normalize longitude to [-180, +180]
        normalized_lon = mod(lon + 180, 360) - 180
        
        new{T}(lat, normalized_lon, alt, pres, loc_err)
    end
end

# Convenience constructor
ObservationLocation(lat, lon, alt = 0.0, pres = 0.0, loc_err = [0.01, 0.01, 10.0]) = 
    ObservationLocation{Float64}(lat, lon, alt, pres, SVector{3,Float64}(loc_err))

"""
    QualityControlFlags

Comprehensive quality control flag structure for observations.

# Fields
- `gross_error_flag::Int`: Gross error check result (0=good, 1=suspect, 2=bad)
- `background_check_flag::Int`: Background check result (0=good, 1=suspect, 2=bad)
- `buddy_check_flag::Int`: Spatial consistency check (0=good, 1=suspect, 2=bad)  
- `domain_check_flag::Int`: Model domain check (0=inside, 1=boundary, 2=outside)
- `time_check_flag::Int`: Temporal consistency check (0=good, 1=suspect, 2=bad)
- `duplicate_flag::Int`: Duplicate observation flag (0=unique, 1=duplicate)
- `analysis_usage_flag::Int`: Analysis usage (1=use, 0=monitor, -1=reject)
- `observation_error_flag::Int`: Observation error assignment flag
- `preprocessing_flag::Int`: Preprocessing operations applied
- `instrument_flag::Int`: Instrument-specific quality information
"""
struct QualityControlFlags
    gross_error_flag::Int
    background_check_flag::Int
    buddy_check_flag::Int
    domain_check_flag::Int
    time_check_flag::Int
    duplicate_flag::Int
    analysis_usage_flag::Int
    observation_error_flag::Int
    preprocessing_flag::Int
    instrument_flag::Int
    
    function QualityControlFlags(; gross_error::Int = 0, background_check::Int = 0,
                                buddy_check::Int = 0, domain_check::Int = 0,
                                time_check::Int = 0, duplicate::Int = 0,
                                analysis_usage::Int = 1, observation_error::Int = 0,
                                preprocessing::Int = 0, instrument::Int = 0)
        new(gross_error, background_check, buddy_check, domain_check, time_check,
            duplicate, analysis_usage, observation_error, preprocessing, instrument)
    end
end

"""
    ObservationError{T<:AbstractFloat}

Comprehensive observation error specification.

# Fields
- `instrument_error::T`: Instrument measurement error standard deviation
- `representativeness_error::T`: Spatial/temporal representativeness error
- `preprocessing_error::T`: Error introduced by preprocessing operations
- `total_error::T`: Total observation error (computed or specified)
- `error_correlation_length::T`: Spatial error correlation length scale
- `error_correlation_time::T`: Temporal error correlation time scale
"""
struct ObservationError{T<:AbstractFloat}
    instrument_error::T
    representativeness_error::T
    preprocessing_error::T
    total_error::T
    error_correlation_length::T
    error_correlation_time::T
    
    function ObservationError{T}(instrument::T, representativeness::T = zero(T),
                                preprocessing::T = zero(T), 
                                correlation_length::T = T(50_000),  # 50 km default
                                correlation_time::T = T(3600)) where T     # 1 hour default
        total = sqrt(instrument^2 + representativeness^2 + preprocessing^2)
        new{T}(instrument, representativeness, preprocessing, total,
               correlation_length, correlation_time)
    end
end

# Convenience constructor
ObservationError(instrument, representativeness = 0.0, preprocessing = 0.0,
                correlation_length = 50_000.0, correlation_time = 3600.0) =
    ObservationError{Float64}(instrument, representativeness, preprocessing,
                             correlation_length, correlation_time)

"""
    AbstractObservation{T<:AbstractFloat}

Abstract base type for all atmospheric observations.
"""
abstract type AbstractObservation{T<:AbstractFloat} end

"""
    ConventionalObservation{T<:AbstractFloat}

Abstract base type for conventional (in-situ) atmospheric observations.
"""
abstract type ConventionalObservation{T<:AbstractFloat} <: AbstractObservation{T} end

"""
    SatelliteObservation{T<:AbstractFloat}

Abstract base type for satellite-based atmospheric observations.
"""
abstract type SatelliteObservation{T<:AbstractFloat} <: AbstractObservation{T} end

"""
    SpecializedObservation{T<:AbstractFloat}

Abstract base type for specialized atmospheric observations.
"""
abstract type SpecializedObservation{T<:AbstractFloat} <: AbstractObservation{T} end

"""
    SurfaceObservation{T<:AbstractFloat}

Surface meteorological station observation.

# Fields
- `station_id::String`: WMO station identifier
- `location::ObservationLocation{T}`: Geographic location
- `observation_time::DateTime`: Observation time in UTC
- `variable::ObservationVariable`: Observed atmospheric variable
- `observed_value::T`: Measured value in SI units
- `background_value::T`: Background (first guess) value
- `observation_error::ObservationError{T}`: Comprehensive error specification
- `quality_control::QualityControlFlags`: Quality control flags
- `station_elevation::T`: Station elevation above sea level (meters)
- `station_type::String`: Station type code
- `measurement_method::String`: Measurement method description
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct SurfaceObservation{T<:AbstractFloat} <: ConventionalObservation{T}
    station_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    variable::ObservationVariable
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    station_elevation::T
    station_type::String
    measurement_method::String
    metadata::Dict{String,Any}
end

"""
    RadiosondeObservation{T<:AbstractFloat}

Upper-air radiosonde observation at a specific pressure level.

# Fields
- `launch_site_id::String`: Radiosonde launch site identifier
- `location::ObservationLocation{T}`: Geographic location at observation level
- `observation_time::DateTime`: Observation time in UTC
- `launch_time::DateTime`: Balloon launch time in UTC
- `variable::ObservationVariable`: Observed atmospheric variable
- `observed_value::T`: Measured value in SI units
- `background_value::T`: Background (first guess) value
- `observation_error::ObservationError{T}`: Comprehensive error specification
- `quality_control::QualityControlFlags`: Quality control flags
- `pressure_level::T`: Pressure level in Pascals
- `level_type::Symbol`: Level type (:surface, :mandatory, :significant, :wind)
- `balloon_type::String`: Balloon and instrument type
- `ascent_rate::T`: Balloon ascent rate (m/s)
- `time_since_launch::T`: Time since balloon launch (seconds)
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct RadiosondeObservation{T<:AbstractFloat} <: ConventionalObservation{T}
    launch_site_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    launch_time::DateTime
    variable::ObservationVariable
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    pressure_level::T
    level_type::Symbol
    balloon_type::String
    ascent_rate::T
    time_since_launch::T
    metadata::Dict{String,Any}
end

"""
    AircraftObservation{T<:AbstractFloat}

Aircraft-based meteorological observation (ACARS, AMDAR).

# Fields
- `aircraft_id::String`: Aircraft identification code
- `location::ObservationLocation{T}`: Geographic location at observation time
- `observation_time::DateTime`: Observation time in UTC
- `variable::ObservationVariable`: Observed atmospheric variable
- `observed_value::T`: Measured value in SI units
- `background_value::T`: Background (first guess) value
- `observation_error::ObservationError{T}`: Comprehensive error specification
- `quality_control::QualityControlFlags`: Quality control flags
- `flight_level::T`: Aircraft flight level (meters above sea level)
- `aircraft_type::String`: Aircraft type designation
- `data_source::String`: Data source (ACARS, AMDAR, etc.)
- `flight_phase::Symbol`: Flight phase (:climb, :cruise, :descent)
- `air_speed::T`: True air speed (m/s)
- `ground_speed::T`: Ground speed (m/s)
- `turbulence_indicator::Int`: Turbulence intensity indicator
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct AircraftObservation{T<:AbstractFloat} <: ConventionalObservation{T}
    aircraft_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    variable::ObservationVariable
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    flight_level::T
    aircraft_type::String
    data_source::String
    flight_phase::Symbol
    air_speed::T
    ground_speed::T
    turbulence_indicator::Int
    metadata::Dict{String,Any}
end

"""
    RadianceObservation{T<:AbstractFloat}

Satellite radiance observation from microwave, infrared, or visible sensors.

# Fields
- `satellite_id::String`: Satellite platform identifier
- `instrument_id::String`: Instrument identifier (AMSU-A, IASI, etc.)
- `location::ObservationLocation{T}`: Geographic location at nadir point
- `observation_time::DateTime`: Observation time in UTC
- `channel::Int`: Instrument channel number
- `observed_value::T`: Measured brightness temperature or radiance
- `background_value::T`: Background (simulated) value
- `observation_error::ObservationError{T}`: Comprehensive error specification
- `quality_control::QualityControlFlags`: Quality control flags
- `scan_position::Int`: Scan position across swath
- `scan_angle::T`: Scan angle from nadir (degrees)
- `solar_zenith_angle::T`: Solar zenith angle (degrees)
- `satellite_zenith_angle::T`: Satellite zenith angle (degrees)
- `solar_azimuth_angle::T`: Solar azimuth angle (degrees)
- `satellite_azimuth_angle::T`: Satellite azimuth angle (degrees)
- `surface_type::Symbol`: Surface type (:land, :ocean, :sea_ice, :mixed)
- `cloud_fraction::T`: Cloud fraction (0-1)
- `surface_temperature::T`: Surface skin temperature (K)
- `bias_correction::T`: Applied bias correction value
- `limb_correction::T`: Limb correction factor
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct RadianceObservation{T<:AbstractFloat} <: SatelliteObservation{T}
    satellite_id::String
    instrument_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    channel::Int
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    scan_position::Int
    scan_angle::T
    solar_zenith_angle::T
    satellite_zenith_angle::T
    solar_azimuth_angle::T
    satellite_azimuth_angle::T
    surface_type::Symbol
    cloud_fraction::T
    surface_temperature::T
    bias_correction::T
    limb_correction::T
    metadata::Dict{String,Any}
end

"""
    GPSROObservation{T<:AbstractFloat}

GPS Radio Occultation observation (refractivity or bending angle).

# Fields
- `gps_satellite_id::String`: GPS satellite identifier  
- `leo_satellite_id::String`: LEO satellite identifier
- `location::ObservationLocation{T}`: Geographic location of ray perigee
- `observation_time::DateTime`: Observation time in UTC
- `variable::ObservationVariable`: Observed variable (refractivity or bending angle)
- `observed_value::T`: Measured value
- `background_value::T`: Background (simulated) value
- `observation_error::ObservationError{T}`: Comprehensive error specification  
- `quality_control::QualityControlFlags`: Quality control flags
- `impact_parameter::T`: Ray impact parameter (meters)
- `bending_angle::T`: Ray bending angle (radians)
- `refractivity::T`: Atmospheric refractivity (N-units)
- `ray_azimuth::T`: Ray azimuth angle (degrees)
- `occultation_type::Symbol`: Occultation type (:rising, :setting)
- `processing_center::String`: Data processing center
- `retrieval_algorithm::String`: Retrieval algorithm identifier
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct GPSROObservation{T<:AbstractFloat} <: SpecializedObservation{T}
    gps_satellite_id::String
    leo_satellite_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    variable::ObservationVariable
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    impact_parameter::T
    bending_angle::T
    refractivity::T
    ray_azimuth::T
    occultation_type::Symbol
    processing_center::String
    retrieval_algorithm::String
    metadata::Dict{String,Any}
end

"""
    RadarObservation{T<:AbstractFloat}

Weather radar observation (reflectivity or radial velocity).

# Fields
- `radar_id::String`: Radar station identifier
- `location::ObservationLocation{T}`: Geographic location of observation volume
- `observation_time::DateTime`: Observation time in UTC
- `variable::ObservationVariable`: Observed variable (reflectivity or velocity)
- `observed_value::T`: Measured value (dBZ or m/s)
- `background_value::T`: Background (simulated) value
- `observation_error::ObservationError{T}`: Comprehensive error specification
- `quality_control::QualityControlFlags`: Quality control flags
- `radar_location::ObservationLocation{T}`: Radar site location
- `range_to_target::T`: Range from radar to observation volume (meters)
- `azimuth_angle::T`: Radar azimuth angle (degrees from north)
- `elevation_angle::T`: Radar elevation angle (degrees from horizontal)
- `beam_width::T`: Radar beam width (degrees)
- `pulse_length::T`: Radar pulse length (microseconds)
- `nyquist_velocity::T`: Nyquist velocity for Doppler measurements (m/s)
- `volume_coverage_pattern::Int`: Radar volume coverage pattern
- `radar_wavelength::T`: Radar wavelength (meters)
- `calibration_offset::T`: Radar calibration offset
- `metadata::Dict{String,Any}`: Additional observation-specific metadata
"""
struct RadarObservation{T<:AbstractFloat} <: SpecializedObservation{T}
    radar_id::String
    location::ObservationLocation{T}
    observation_time::DateTime
    variable::ObservationVariable
    observed_value::T
    background_value::T
    observation_error::ObservationError{T}
    quality_control::QualityControlFlags
    radar_location::ObservationLocation{T}
    range_to_target::T
    azimuth_angle::T
    elevation_angle::T
    beam_width::T
    pulse_length::T
    nyquist_velocity::T
    volume_coverage_pattern::Int
    radar_wavelength::T
    calibration_offset::T
    metadata::Dict{String,Any}
end

"""
    ObservationCollection{T<:AbstractFloat}

Efficient collection of observations with indexing and search capabilities.

# Fields
- `observations::Vector{AbstractObservation{T}}`: Vector of observation objects
- `spatial_index::Dict{Tuple{Int,Int},Vector{Int}}`: Spatial grid index for fast lookups
- `temporal_index::Dict{DateTime,Vector{Int}}`: Temporal index for time-based searches
- `type_index::Dict{Type,Vector{Int}}`: Type-based index for observation type filtering
- `variable_index::Dict{ObservationVariable,Vector{Int}}`: Variable-based index
- `platform_index::Dict{String,Vector{Int}}`: Platform-based index
- `grid_resolution::T`: Spatial grid resolution for indexing (degrees)
- `total_count::Int`: Total number of observations in collection
"""
mutable struct ObservationCollection{T<:AbstractFloat}
    observations::Vector{AbstractObservation{T}}
    spatial_index::Dict{Tuple{Int,Int},Vector{Int}}
    temporal_index::Dict{DateTime,Vector{Int}}
    type_index::Dict{Type,Vector{Int}}
    variable_index::Dict{ObservationVariable,Vector{Int}}
    platform_index::Dict{String,Vector{Int}}
    grid_resolution::T
    total_count::Int
    
    function ObservationCollection{T}(grid_resolution::T = T(1.0)) where T
        new{T}(
            Vector{AbstractObservation{T}}(),
            Dict{Tuple{Int,Int},Vector{Int}}(),
            Dict{DateTime,Vector{Int}}(),
            Dict{Type,Vector{Int}}(),
            Dict{ObservationVariable,Vector{Int}}(),
            Dict{String,Vector{Int}}(),
            grid_resolution,
            0
        )
    end
end

# Convenience constructor
ObservationCollection(grid_resolution = 1.0) = ObservationCollection{Float64}(grid_resolution)

"""
    Base.push!(collection::ObservationCollection{T}, obs::AbstractObservation{T}) where T

Add an observation to the collection and update indices.
"""
function Base.push!(collection::ObservationCollection{T}, obs::AbstractObservation{T}) where T
    push!(collection.observations, obs)
    collection.total_count += 1
    
    idx = collection.total_count
    
    # Update spatial index
    lat_bin = round(Int, obs.location.latitude / collection.grid_resolution)
    lon_bin = round(Int, obs.location.longitude / collection.grid_resolution)
    spatial_key = (lat_bin, lon_bin)
    
    if haskey(collection.spatial_index, spatial_key)
        push!(collection.spatial_index[spatial_key], idx)
    else
        collection.spatial_index[spatial_key] = [idx]
    end
    
    # Update temporal index (round to nearest hour)
    time_key = DateTime(Dates.year(obs.observation_time), Dates.month(obs.observation_time),
                       Dates.day(obs.observation_time), Dates.hour(obs.observation_time))
    
    if haskey(collection.temporal_index, time_key)
        push!(collection.temporal_index[time_key], idx)
    else
        collection.temporal_index[time_key] = [idx]
    end
    
    # Update type index
    obs_type = typeof(obs)
    if haskey(collection.type_index, obs_type)
        push!(collection.type_index[obs_type], idx)
    else
        collection.type_index[obs_type] = [idx]
    end
    
    # Update variable index
    if haskey(collection.variable_index, obs.variable)
        push!(collection.variable_index[obs.variable], idx)
    else
        collection.variable_index[obs.variable] = [idx]
    end
    
    # Update platform index (extract platform from observation)
    platform_id = get_platform_id(obs)
    if haskey(collection.platform_index, platform_id)
        push!(collection.platform_index[platform_id], idx)
    else
        collection.platform_index[platform_id] = [platform_id]
    end
    
    return collection
end

"""
    filter_observations(collection::ObservationCollection{T}, 
                       criteria::Dict{Symbol,Any}) where T

Filter observations based on specified criteria.

# Arguments
- `collection::ObservationCollection{T}`: Observation collection to filter
- `criteria::Dict{Symbol,Any}`: Filtering criteria

# Supported Criteria
- `:variable`: Filter by observation variable
- `:time_range`: Filter by time range (Tuple{DateTime,DateTime})
- `:spatial_bounds`: Filter by geographic bounds (lat_min, lat_max, lon_min, lon_max)
- `:observation_type`: Filter by observation type  
- `:quality_flag`: Filter by quality control flags
- `:platform`: Filter by platform identifier
- `:analysis_usage`: Filter by analysis usage flag

# Returns
- `Vector{AbstractObservation{T}}`: Filtered observations

# Examples
```julia
# Filter for temperature observations used in analysis
temp_obs = filter_observations(collection, Dict(
    :variable => TEMPERATURE,
    :analysis_usage => true
))

# Filter by time and location
regional_obs = filter_observations(collection, Dict(
    :time_range => (DateTime(2024,1,1,12), DateTime(2024,1,1,18)),
    :spatial_bounds => (30.0, 50.0, -120.0, -80.0)  # lat_min, lat_max, lon_min, lon_max
))
```
"""
function filter_observations(collection::ObservationCollection{T}, 
                           criteria::Dict{Symbol,Any}) where T
    filtered_indices = Set(1:collection.total_count)
    
    # Apply variable filter
    if haskey(criteria, :variable)
        variable_criteria = criteria[:variable]
        if haskey(collection.variable_index, variable_criteria)
            intersect!(filtered_indices, Set(collection.variable_index[variable_criteria]))
        else
            return AbstractObservation{T}[]  # No observations match
        end
    end
    
    # Apply observation type filter
    if haskey(criteria, :observation_type)
        type_criteria = criteria[:observation_type]
        if haskey(collection.type_index, type_criteria)
            intersect!(filtered_indices, Set(collection.type_index[type_criteria]))
        else
            return AbstractObservation{T}[]
        end
    end
    
    # Apply time range filter
    if haskey(criteria, :time_range)
        time_start, time_end = criteria[:time_range]
        time_filtered = Int[]
        
        for (time_key, indices) in collection.temporal_index
            if time_start <= time_key <= time_end
                append!(time_filtered, indices)
            end
        end
        
        intersect!(filtered_indices, Set(time_filtered))
    end
    
    # Apply spatial bounds filter
    if haskey(criteria, :spatial_bounds)
        lat_min, lat_max, lon_min, lon_max = criteria[:spatial_bounds]
        spatial_filtered = Int[]
        
        for (spatial_key, indices) in collection.spatial_index
            lat_bin, lon_bin = spatial_key
            lat_center = lat_bin * collection.grid_resolution
            lon_center = lon_bin * collection.grid_resolution
            
            if lat_min <= lat_center <= lat_max && lon_min <= lon_center <= lon_max
                append!(spatial_filtered, indices)
            end
        end
        
        intersect!(filtered_indices, Set(spatial_filtered))
    end
    
    # Apply additional filters on the remaining observations
    result = AbstractObservation{T}[]
    
    for idx in filtered_indices
        obs = collection.observations[idx]
        include_obs = true
        
        # Quality flag filter
        if haskey(criteria, :quality_flag)
            qc_criteria = criteria[:quality_flag]
            if obs.quality_control.analysis_usage_flag != qc_criteria
                include_obs = false
            end
        end
        
        # Analysis usage filter
        if haskey(criteria, :analysis_usage) && include_obs
            usage_criteria = criteria[:analysis_usage]
            if (obs.quality_control.analysis_usage_flag > 0) != usage_criteria
                include_obs = false
            end
        end
        
        if include_obs
            push!(result, obs)
        end
    end
    
    return result
end

# Convenience constructors for common observation types

"""
    create_surface_observation(station_id::String, lat::T, lon::T, elevation::T,
                              time::DateTime, variable::ObservationVariable, value::T;
                              obs_error::T = T(1.0)) where T

Create a surface observation with default settings.
"""
function create_surface_observation(station_id::String, lat::T, lon::T, elevation::T,
                                   time::DateTime, variable::ObservationVariable, value::T;
                                   obs_error::T = T(1.0)) where T
    location = ObservationLocation{T}(lat, lon, elevation)
    error_spec = ObservationError{T}(obs_error)
    qc_flags = QualityControlFlags()
    
    return SurfaceObservation{T}(
        station_id, location, time, variable, value, zero(T),
        error_spec, qc_flags, elevation, "SYNOP", "automatic",
        Dict{String,Any}()
    )
end

"""
    create_radiance_observation(satellite::String, instrument::String, channel::Int,
                               lat::T, lon::T, time::DateTime, brightness_temp::T;
                               obs_error::T = T(0.5)) where T

Create a satellite radiance observation with default settings.
"""
function create_radiance_observation(satellite::String, instrument::String, channel::Int,
                                    lat::T, lon::T, time::DateTime, brightness_temp::T;
                                    obs_error::T = T(0.5)) where T
    location = ObservationLocation{T}(lat, lon)
    error_spec = ObservationError{T}(obs_error)
    qc_flags = QualityControlFlags()
    
    return RadianceObservation{T}(
        satellite, instrument, location, time, channel, brightness_temp, zero(T),
        error_spec, qc_flags, 1, zero(T), zero(T), zero(T), zero(T), zero(T),
        :ocean, zero(T), T(280), zero(T), zero(T), Dict{String,Any}()
    )
end

# Helper functions

"""
    get_platform_id(obs::AbstractObservation) -> String

Extract platform identifier from observation.
"""
function get_platform_id(obs::SurfaceObservation)::String
    return obs.station_id
end

function get_platform_id(obs::RadiosondeObservation)::String
    return obs.launch_site_id
end

function get_platform_id(obs::AircraftObservation)::String
    return obs.aircraft_id
end

function get_platform_id(obs::RadianceObservation)::String
    return obs.satellite_id
end

function get_platform_id(obs::GPSROObservation)::String
    return obs.leo_satellite_id
end

function get_platform_id(obs::RadarObservation)::String
    return obs.radar_id
end

function get_platform_id(obs::AbstractObservation)::String
    return "unknown"
end

"""
    is_analysis_ready(obs::AbstractObservation) -> Bool

Check if observation is ready for analysis (passes quality control).
"""
function is_analysis_ready(obs::AbstractObservation)::Bool
    return (obs.quality_control.analysis_usage_flag > 0 &&
            obs.quality_control.gross_error_flag == 0 &&
            obs.quality_control.background_check_flag <= 1)
end

end # module ObservationTypes